/*
 * Copyright (C) Francisco Morero Peyrona. All rights reserved.
 *
 * This software is published under the terms of Open Source 
 * License version 1.1, a copy of which has been included with this
 * distribution in the License.txt file.  
 */

package jdbcmanager.dbtools.agentwise;

import java.io.*;
import java.util.*;

import jdbcmanager.app.*;

/**
 * Loads from and saves into a file information about a DBMS agent.
 *   
 * @author Francisco Morero Peyrona
 */
final class AgentDefinitionIO
{
   private static final String sFILE_EXTENSION = ".dbms";
   private final static String sDIR_MACRO      = "<app_def>";  // Used in *.dbms files (JARs entry) to point 
                                                               // to drivers dir inside JDBC Explorer dir.
   private final static String sDRIVER     = "DriverName";
   private final static String sDRIVER_ID  = "DriverID";
   private final static String sCLASS      = "DriverClassName";
   private final static String sSYNTAX     = "ConnectionSyntax";
   private final static String sJAR        = "JARFiles";
   private final static String sAGENT_SQL  = "AgentSQLClassName";
   private final static String sAGENT_META = "AgentMetaDataClassName";
   private final static String sAGENTS_JAR = "AgentsJARName";
   
   private Properties props;
   
   /**
    * Class contructor: invoked when creating a new Driver (there is no file) 
    * <p>
    * Properties are sat using the set... methods, and file is created by calling
    * saveAs( ... ) method.
    * 
    * @param f
    * @throws IOException
    */
   AgentDefinitionIO()
   {
      this.props = new Properties();
   }

   /**
    * Class contructor: invoked when loading an existing file 
    * 
    * @param  f
    * @throws IOException
    */
   AgentDefinitionIO( File f ) throws IOException
   {
      this.props = new Properties();
      this.props.load( new FileInputStream( f ) );
   }

   static File[] getAgentDefinitions()
   {
      File[] aFile = App.getDriversDir().listFiles( new FileFilter()
      {
         public boolean accept( File f )  
         { 
            return f.isFile() && f.getName().endsWith( sFILE_EXTENSION ); 
         }
      } );

      return aFile;
   }

   //-------------------------------------------------------------------------//
   // GET COMMANDS

   /**
    * The name of the DB Server (e.g. "Postgre SQL")
    * <p>
    * This is the name as it is stored in the DBMS definition file, that should be 
    * equals or very similar to the one returnred by <code>connection.getMetaData().getDatabaseProductName()</code>.
    * <p> 
    * I can not use "getMetaData.getProductName()", because when "getProductName()" is invoked, 
    * the connection still does not exists.
    *
    * @return The name of the DB Server
    */
   String getDriverName()
   {
      return this.props.getProperty( sDRIVER, "Unknown DBMS name" );   
   }
   
   /**
    * Unique string that identifies the driver.
    * 
    * @return Unique string that identifies the driver.
    */
   String getDriverID()
   {
      return this.props.getProperty( sDRIVER_ID, "" );
   } 

   /**
    * The argument used when calling <code>Class.forname( ... )</code> method.
    * 
    * @return The argument used when calling <code>Class.forname( ... )</code> method
    */
   String getDriverClassName()
   {
      return this.props.getProperty( sCLASS, "" );
   }
   
   /**
    * Syntax to be used to stablish the connection.<br>
    * Just to help the application user.
    * 
    * @return Syntax to be used to stablish the connection
    */
   String getConnectionSyntax()
   {
      return this.props.getProperty( sSYNTAX, "" );
   }
   
   /**
    * The JAR(s) file(s) where the JDBC driver is implemented
    * 
    * @return
    */
   String[] getDriverJARs()
   {
      String[] asJAR   = StrWise.string2Array( this.props.getProperty( sJAR, "" ).trim(), ";" );   
      String   sDrvDir = App.getDriversDir().getAbsolutePath() + File.separator;
      
      for( int n = 0; n < asJAR.length; n++ )
      {         
         if( asJAR[n].startsWith( sDIR_MACRO ) )
            asJAR[n] = StrWise.replaceFirst( asJAR[n], sDIR_MACRO, sDrvDir );
      }
      
      return asJAR;
   }

   /**
    * 
    * @return
    */
   String getAgentSQLClassName()
   {
      return checkAgentName( this.props.getProperty( sAGENT_SQL, "" ), "AgentSQLDefault" );
   }
   
   /**
    * 
    * @return
    */
   String getAgentMetaDataClassName()
   {
      return checkAgentName( this.props.getProperty( sAGENT_META, "" ), "AgentMetaDataDefault" );
   }
   
   /**
    * 
    * @return
    */
   String getAgentsJAR()
   {
      String sJAR = this.props.getProperty( sAGENTS_JAR, "" );
      
      if( sJAR.length() > 0 && (! sJAR.toUpperCase().endsWith( ".JAR" ) ) )
         sJAR += ".jar";
      
      return sJAR;
   }
   
   /**
    * true if the file contains the minimum information to work.<br>
    * Note: it can has the informaiton, but can be wrong information: this is not checked.
    * It could also happen that the key exists but not the value for the key: this is not checked.
    *  
    * @return true if the file contains the minimum information to work.
    */
   // Has to be checked everytime this method is called because properties can change on the fly, not only at creation
   boolean isWellFormed()
   {
      String[] asKey = { sDRIVER, sDRIVER_ID, sCLASS, sJAR };
      int n = 0;
      
      while( n < asKey.length )
      {
         if( ! this.props.containsKey( asKey[n] ) )
            break;
            
         n++;
      }
      
      return n >= asKey.length;
   }
   
   //-------------------------------------------------------------------------//
   // SET COMMANDS
   
   /**
    * 
    * @param sName
    */
   void setDriverName( String sName )
   {
      this.props.put( sDRIVER, sName.trim() );   
   } 
   
   /**
    * 
    * @param sID
    */
   void setDriverID( String sID )
   {
      this.props.put( sDRIVER_ID, sID.trim() );
   }
   
   /**
    * 
    * @param sClass
    */
   void setDriverClassName( String sClass )
   {
      this.props.put( sCLASS, sClass.trim() );
   }
   
   /**
    * 
    * @param sSyntax
    */
   void setConnectionSyntax( String sSyntax )
   {
      this.props.put( sSYNTAX, sSyntax.trim() );
   }
   
   /**
    * 
    * @param sClass
    */
   void setAgentMetaDataClassName( String sClass )
   {
      this.props.put( sAGENT_META, sClass.trim() );
   }
   
   /**
    * 
    * @param sClass
    */
   void setAgentSQLClassName( String sClass )
   {
      this.props.put( sAGENT_SQL, sClass.trim() );
   }
   
   /**
    * 
    * @param sJAR
    */
   void setAgentsJAR( String sJAR )
   {
      this.props.put( sAGENTS_JAR, sJAR.trim() );
   }
   
   /**
    * 
    * @param asJAR
    */
   void setDriverJARs( String[] asJAR )
   {
      this.props.put( sJAR, driversArray2String( asJAR ) );
   }
   
   /**
    * Saves the Agent IO definition into a file.<br>
    * The name of the file is created automatically based on Product's name.
    */
   void save()
   {
      try
      {
         String         sFile = StrWise.normalizeFileName( getDriverName() ) + sFILE_EXTENSION;
         File           fOut  = new File( App.getDriversDir(), sFile );
         String         sID   = getDriverID();
         String         sJARs = driversArray2String( getDriverJARs() );
         BufferedWriter bw    = new BufferedWriter( new FileWriter( fOut, false ) );   // Must be the last line
         
         // I write the file because Properties.store() does is really ugly
         bw.write( "# " + getDriverName() + " - Driver definition file"                ); bw.newLine();
         bw.newLine();
         bw.write( StrWise.padR( sDRIVER    , 23 ) +" = "+ getDriverName()             ); bw.newLine();
         bw.write( StrWise.padR( sDRIVER_ID , 23 ) +" = "+ sID                         ); bw.newLine();
         bw.write( StrWise.padR( sCLASS     , 23 ) +" = "+ getDriverClassName()        ); bw.newLine();
         bw.write( StrWise.padR( sSYNTAX    , 23 ) +" = "+ getConnectionSyntax()       ); bw.newLine();
         bw.write( StrWise.padR( sJAR       , 23 ) +" = "+ sJARs                       ); bw.newLine();
         bw.newLine();
         bw.write( StrWise.padR( sAGENT_SQL , 23 ) +" = "+ getAgentSQLClassName()      ); bw.newLine();
         bw.write( StrWise.padR( sAGENT_META, 23 ) +" = "+ getAgentMetaDataClassName() ); bw.newLine();
         bw.write( StrWise.padR( sAGENTS_JAR, 23 ) +" = "+ getAgentsJAR()              ); bw.newLine();
         
         bw.close();
      }
      catch( IOException exc )
      {
         App.showException( exc, "Error saving driver definition file" );
      }
   }
   
   //-------------------------------------------------------------------------//
   
   private String checkAgentName( String sName, String sDefault )
   {
      if( sName.length() == 0 )  // Either the key does not exists or it is empty
         sName = sDefault;
         
      if( ! sName.endsWith( ".class" ) )
         sName += ".class";
     
      return sName;     
   }
   
   private String driversArray2String( String[] asJAR )
   {
      StringBuffer sb = new StringBuffer( 1024 );
      String  sDriversDir = App.getDriversDir().getAbsolutePath() + File.separator;

      for( int n = 0; n < asJAR.length; n++ )
      {
         if( asJAR[n].startsWith( sDriversDir ) )
            asJAR[n] = StrWise.replaceFirst( asJAR[n], sDriversDir, sDIR_MACRO );
   
         sb.append( asJAR[n] ).append( ';' );
      }

      sb.deleteCharAt( sb.length() - 1 );   // Deletes last ';'
      
      return sb.toString();
   }
}
